/*
 * ArchivoBloques.cpp
 *
 *  Created on: 22/04/2013
 *      Author: pancho
 */

#include "ArchivoBloques.h"

#include <iostream>
#include <math.h>

using namespace std;

ArchivoBloques::ArchivoBloques(string path, string tipo, unsigned int tamBloque) {
	string pathBitmap = path + "bitmap.bin";

	this->archivo.open(path.c_str(), fstream::in | fstream::out | fstream::binary);
	if(!this->archivo.is_open()) { // crear archivo
		cout << "No se encontro el archivo "<< path <<" se creara uno vacio." << endl;
		this->archivo.open(path.c_str(), fstream::in | fstream::out | fstream::binary | fstream::trunc);
		if(!this->archivo) {
			cout << "No se se pudo crear el archivo en la ruta indicada." << endl;
		}
		this->tamanioBloque = tamBloque;
		this->buffer = new char[this->tamanioBloque];

		this->archBitmap.open(pathBitmap.c_str(), fstream::in | fstream::out | fstream::binary | fstream::trunc);
		tamanioBitmap = 0;

		this->cantidadBloques = 0;

		if(tipo == "char") this->codigoTipo = 0;
		else if (tipo == "string") this->codigoTipo = 1;
		else if (tipo == "unsigned short int") this->codigoTipo = 2;
		else if (tipo == "unsigned char") this->codigoTipo = 3;
		else if (tipo == "float") this->codigoTipo = 4;
		else if (tipo == "double") this->codigoTipo = 5;
		else if (tipo == "int") this->codigoTipo = 6;
		else if (tipo == "long int") this->codigoTipo = 7;
		else if (tipo == "char*") this->codigoTipo = 8;
		else if (tipo == "void") this->codigoTipo = 9;

		this->archivo.write((char*)&this->codigoTipo, sizeof(unsigned char));
		this->archivo.write((char*)&tamBloque, sizeof(unsigned int));
	}
	else { //abrir archivo
		this->archivo.seekg(ios_base::beg);
		this->archivo.read((char*)&(this->codigoTipo), sizeof(unsigned char));
		this->archivo.read((char*)&(this->tamanioBloque), sizeof(unsigned int));
		this->archivo.read((char*)&(this->cantidadBloques), sizeof(unsigned short int));
		this->buffer = new char[this->tamanioBloque];

		this->archBitmap.open(pathBitmap.c_str(), fstream::in | fstream::out | fstream::binary);
		this->archBitmap.seekg(0, ios_base::end);
		tamanioBitmap = this->archBitmap.tellg();
		tamanioBitmap += 1;
		this->bitmap = new char[tamanioBitmap];
		this->archBitmap.seekg(0, ios_base::beg);
		this->archBitmap.read(this->bitmap, tamanioBitmap);
	}
}

string ArchivoBloques::getTipo() {
	string tipo;
	switch(this->codigoTipo) {
		case 0: tipo = "char"; break;
		case 1: tipo = "string"; break;
		case 2: tipo = "unsigned short int"; break;
		case 3: tipo = "unsigned char"; break;
		case 4: tipo = "float"; break;
		case 5: tipo = "double"; break;
		case 6: tipo = "int"; break;
		case 7: tipo = "long int"; break;
		case 8: tipo = "char*"; break;
		case 9: tipo = "void"; break;
	}
	return tipo;
}

ArchivoBloques::ArchivoBloques(string path) {
	this->archivo.open(path.c_str(), fstream::in | fstream::out | fstream::binary);
	if(!this->archivo.good()) {
		cout << "Se ha producido un error al intentar abrir el archivo" << endl;
	}
	string pathBitmap = path + "bitmap.bin";
	this->archBitmap.open(pathBitmap.c_str(), fstream::in | fstream::out | fstream::binary);
	if(!this->archivo.good()) {
		cout << "Se ha producido un error al intentar abrir el archivo" << endl;
	}
}

int ArchivoBloques::AgregarBloque(char *info, unsigned int tam) {
	if (tam > this->tamanioBloque) {
		cout << "Demasiada informacion para el bloque" << endl;
		return -1;
	}

	unsigned int numeroBloque = 0;
	bool bloqueLibre = false;
	unsigned int i = 0;
	if(tamanioBitmap != 0) {
		while (bitmap[i] == 0 && i < tamanioBitmap - 1)
			i++;
		if(bitmap[i] != 0)
			bloqueLibre = true;
		if(bloqueLibre) { // agrego la info en la posicion libre
			int bit = 0;
			char aux = 1;
			//int sss = bitmap[i] & aux;
			//cout << "and:" << sss << endl;
			if ((bitmap[i] & aux) == 1) bit = 1;
			else {
				aux = 2;
				if ((bitmap[i] & aux) == 2) bit = 2;
				else {
					aux = 4;
					if ((bitmap[i] & aux) == 4) bit = 3;
					else {
						aux = 8;
						if ((bitmap[i] & aux) == 8) bit = 4;
						else {
							aux = 16;
							if ((bitmap[i] & aux) == 16) bit = 5;
							else {
								aux = 32;
								if ((bitmap[i] & aux) == 32) bit = 6;
								else {
									aux = 64;
									if ((bitmap[i] & aux) == 64) bit = 6;
									else {
										aux = 128;
										if ((bitmap[i] & aux) == 128) bit = 7;
									}
								}
							}
						}
					}
				}
			}
			numeroBloque = 8 * i - 1 + bit;
			this->ModificarBloque(numeroBloque, info, tam);
			this->MarcarBloqueOcuapdo(numeroBloque);
		}
	}
	else { //agrego un bloque al final del archivo
		unsigned int offset = tamanioMetadata;
		if(this->cantidadBloques != 0)
		 	 offset += this->cantidadBloques * this->tamanioBloque;
		this->archivo.seekg(offset, ios_base::beg);
		numeroBloque = this->cantidadBloques;
		this->cantidadBloques++;
		this->archivo.write(info, tam);

		this->archBitmap.seekg(0, ios_base::end);
		if((cantidadBloques / 8) > this->archBitmap.tellg()) {
			this->tamanioBitmap++;
			char* aux = new char[tamanioBitmap];
			for (unsigned int i = 0;i < tamanioBitmap;i++)
				aux[i] = bitmap[i];
			aux[tamanioBitmap] = 0;
			delete bitmap;
			bitmap = aux;
		}
	}
	return numeroBloque;
}

char* ArchivoBloques::RecuperarBloque(unsigned int numeroBloque) {
	unsigned int offset = tamanioMetadata;
	if(numeroBloque != 0)
		offset += numeroBloque * this->tamanioBloque;
	this->archivo.seekg(offset, ios_base::beg);
	this->archivo.read(buffer, this->tamanioBloque);
	return this->buffer;
}

bool ArchivoBloques::ModificarBloque(unsigned int numeroBloque, char *info, unsigned int tam) {
	if(numeroBloque > this->cantidadBloques)
		return false;
	unsigned int offset = tamanioMetadata;
	if(numeroBloque != 0)
	 	 offset += numeroBloque * this->tamanioBloque;
	this->archivo.seekg(offset, ios_base::beg);
	this->archivo.write(info, tam);
	return true;
}

bool ArchivoBloques::LiberarBloque(unsigned int numeroBloque) {
	if(numeroBloque >= this->cantidadBloques)
		return false;
	//modificar bitmap
	int byte = numeroBloque / 8; //bytes empezando del cero
	int bit = numeroBloque % 8; //bits de 0 a 7
	char aux = 0;
		aux = pow(2, bit);
	bitmap[byte] = bitmap[byte] | aux;

	//cout << "byte: " << byte << "   bit:" << bit << endl;

	//for(int i = 0;i < tamanioBitmap;i++)
	//	cout << (int)bitmap[i] << endl;

	return true;
}

void ArchivoBloques::MarcarBloqueOcuapdo(unsigned int numeroBloque) {
	//modificar bitmap
	int byte = numeroBloque / 8; //bytes empezando del cero
	int bit = numeroBloque % 8; //bits de 0 a 7
	char aux = 0;
	aux = pow(2, bit);
	aux = ~aux;
	bitmap[byte] = bitmap[byte] & aux;

	//cout << "byte: " << byte << "   bit:" << bit << endl;

	//for(int i = 0;i < tamanioBitmap;i++)
	//	cout << (int)bitmap[i] << endl;
}

unsigned short int ArchivoBloques::getCantidadBloques() {
	return this->cantidadBloques;
}

ArchivoBloques::~ArchivoBloques() {
	this->archivo.seekg(sizeof(unsigned char) + sizeof(unsigned int), ios_base::beg);
	this->archivo.write((char*)&(this->cantidadBloques), sizeof(unsigned short int));
	this->archivo.close();
	delete buffer;

	this->archBitmap.seekg(0, ios_base::beg);
	this->archBitmap.write(bitmap, tamanioBitmap);
	this->archBitmap.close();
	delete bitmap;
}
